//
//  PSLevels.m
//  PrefCore
//
//  Created by Sergey on 2/18/14.
//  Copyright (c) 2014 Play-on-smart. All rights reserved.
//

#import "PSLevels.h"
#import "PSErrors.h"

static const NSString* keyPassing = @"passing";
static const NSString* keyTrickingOpen = @"trickingOpen";
static const NSString* keyTrickingClosed = @"trickingStanding";
static const NSString* keyMisere = @"misere";

/// Read or write the level data dictionary.
static NSDictionary *levelData(NSDictionary* newVal);

@implementation PSLevels

+(int)bestMoveDepthWithGame:(PSGame*)game
                      settings:(PSGameSettings*)settings
                     andPlayerId:(int)playerId {
    if (playerId == 0 || (game.hand > 0 && game.lastBid && !game.lastBid.isPass)) {
        return 0;
    }
    NSDictionary *dict = levelData(nil);
    NSArray *table;
    if (!game.lastBid || game.lastBid.isPass) {
        table = [dict objectForKey:keyPassing];
    } else {
        if (game.lastBid.isMisere) {
            table = [dict objectForKey:keyMisere];
        } else {
            if (game.movingTye == kMovingTypeUnknown || game.movingTye == kMovingTypeStanding) {
                table = [dict objectForKey:keyTrickingClosed];
            } else {
                table = [dict objectForKey:keyTrickingOpen];
            }
        }
    }
    PSGameLevel level = [settings levelForPlayerId:playerId];
    NSArray *probabilities = table[level][game.rounds];
    int depth = 0;
    for (NSNumber *pObj in probabilities) {
        NSUInteger p = pObj.integerValue;
        if (p > 0 && arc4random_uniform(100) < p) {
            return depth;
        }
        ++depth;
    }
    return depth;
}

+(BOOL)updateWithData:(NSData*)data error:(NSError **)error {
    NSDictionary *dict =[NSJSONSerialization JSONObjectWithData:data options:0 error:error];
    // Check for a JSON error
    if (dict == NULL) {
        return NO;
    }
    // Checking for data errors:
    // Ensure that the top level is a dictionary
    if (![dict isKindOfClass:[NSDictionary class]]) {
        if (error != NULL) {
            *error = [NSError errorWithDomain:PSPrefErrorDomain
                                         code:PS_ERROR_INVALID_DATA
                                     userInfo:nil];
        }
        return NO;
    }
    // Ensure that all required keys are included
    NSMutableSet *missingKeys = [NSMutableSet setWithArray:
        @[keyPassing, keyTrickingOpen, keyTrickingClosed, keyMisere]
    ];
    for (NSString *key in dict.allKeys) {
        [missingKeys removeObject:key];
    }
    if (missingKeys.count) {
        NSString *missingKeysStr = [missingKeys.allObjects componentsJoinedByString:@", "];
        if (error != NULL) {
            *error = [NSError errorWithDomain:PSPrefErrorDomain
                                         code:PS_ERROR_MISSING_DATA
                                     userInfo:@{PSErrorMissingDataKey:missingKeysStr}];
        }
        return NO;
    }
    // Ensure that all arrays are have dimensions 4×10×N
    BOOL structureCheck = YES;
    for (NSArray *topLevel in dict.allValues) {
        if (![topLevel isKindOfClass:[NSArray class]] || topLevel.count != 4) {
            structureCheck = NO;
            break;
        }
        for (NSArray *midLevel in topLevel) {
            if (![midLevel isKindOfClass:[NSArray class]] || midLevel.count != 10) {
                structureCheck = NO;
                break;
            }
            for (NSArray *a in midLevel) {
                if (![a isKindOfClass:[NSArray class]]) {
                    structureCheck = NO;
                    break;
                }
            }
            if (!structureCheck) {
                break;
            }
        }
        if (!structureCheck) {
            break;
        }
    }
    if (!structureCheck) {
        if (error != NULL) {
            *error = [NSError errorWithDomain:PSPrefErrorDomain
                                         code:PS_ERROR_INVALID_DATA
                                     userInfo:nil];
        }
        return NO;
    }
    // The object has passed validation.
    levelData(dict);
    return YES;
}

@end

static NSDictionary *levelData(NSDictionary* newVal) {
    static NSDictionary *dict = nil;
    if (newVal) {
        return (dict = newVal);
    }
    // If the dict has been set, do not re-initialize
    if (dict == nil) {
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            dict = @{
                keyPassing : @[
                    // L0
                    @[
                        @[@0, @0]
                    ,   @[@0]
                    ,   @[@0]
                    ,   @[@0]
                    ,   @[]
                    ,   @[]
                    ,   @[]
                    ,   @[]
                    ,   @[]
                    ,   @[]
                    ]
                ,   // L1
                    @[
                        @[@0, @0, @0, @0]
                    ,   @[@0, @0, @0]
                    ,   @[@0, @0, @0]
                    ,   @[@0, @0]
                    ,   @[@0, @0]
                    ,   @[@0]
                    ,   @[@0]
                    ,   @[@0]
                    ,   @[]
                    ,   @[]
                    ]
                ,   // L2
                    @[
                        @[@0, @0, @0, @0, @0, @0]
                    ,   @[@0, @0, @0, @0, @0]
                    ,   @[@0, @0, @0, @0]
                    ,   @[@0, @0, @0]
                    ,   @[@0, @0]
                    ,   @[@0, @0]
                    ,   @[@0]
                    ,   @[@0]
                    ,   @[]
                    ,   @[]
                    ]
                ,   // L3
                    @[
                        @[@0, @0, @0, @0, @0, @0, @0, @0, @0]
                    ,   @[@0, @0, @0, @0, @0, @0, @0, @0]
                    ,   @[@0, @0, @0, @0, @0, @0, @0]
                    ,   @[@0, @0, @0, @0, @0, @0]
                    ,   @[@0, @0, @0, @0, @0]
                    ,   @[@0, @0, @0, @0]
                    ,   @[@0, @0, @0]
                    ,   @[@0, @0]
                    ,   @[@0]
                    ,   @[]
                    ]
                ]
            ,   keyTrickingOpen : @[
                    // L0
                    @[
                        @[]
                    ,   @[]
                    ,   @[]
                    ,   @[]
                    ,   @[]
                    ,   @[]
                    ,   @[]
                    ,   @[]
                    ,   @[]
                    ,   @[]
                    ]
                ,   // L1
                    @[
                        @[]
                    ,   @[]
                    ,   @[]
                    ,   @[]
                    ,   @[]
                    ,   @[]
                    ,   @[]
                    ,   @[]
                    ,   @[]
                    ,   @[]
                    ]
                ,   // L2
                    @[
                        @[@0, @0, @0, @0, @0]
                    ,   @[@0, @0, @0, @0]
                    ,   @[@0, @0, @0]
                    ,   @[@0, @0]
                    ,   @[@0]
                    ,   @[]
                    ,   @[]
                    ,   @[]
                    ,   @[]
                    ,   @[]
                    ]
                ,   // L3
                    @[
                        @[@0, @0, @0, @0, @0, @0]
                    ,   @[@0, @0, @0, @0, @0]
                    ,   @[@0, @0, @0, @0]
                    ,   @[@0, @0, @0]
                    ,   @[@0, @0]
                    ,   @[@0]
                    ,   @[@0]
                    ,   @[]
                    ,   @[]
                    ,   @[]
                    ]
                ]
            ,   keyTrickingClosed : @[
                    // L0
                    @[
                        @[]
                    ,   @[]
                    ,   @[]
                    ,   @[]
                    ,   @[]
                    ,   @[]
                    ,   @[]
                    ,   @[]
                    ,   @[]
                    ,   @[]
                    ]
                ,   // L1
                    @[
                        @[@0, @0, @0]
                    ,   @[@0, @0, @0]
                    ,   @[@0, @0]
                    ,   @[@0, @0]
                    ,   @[@0]
                    ,   @[@0]
                    ,   @[]
                    ,   @[]
                    ,   @[]
                    ,   @[]
                    ]
                ,   // L2
                    @[
                        @[@0, @0, @0, @0, @0]
                    ,   @[@0, @0, @0, @0]
                    ,   @[@0, @0, @0]
                    ,   @[@0, @0]
                    ,   @[@0, @0]
                    ,   @[@0]
                    ,   @[@0]
                    ,   @[]
                    ,   @[]
                    ,   @[]
                    ]
                ,   // L3
                    @[
                        @[@0, @0, @0, @0, @0, @0, @0, @0]
                    ,   @[@0, @0, @0, @0, @0, @0, @0]
                    ,   @[@0, @0, @0, @0, @0, @0]
                    ,   @[@0, @0, @0, @0, @0]
                    ,   @[@0, @0, @0, @0]
                    ,   @[@0, @0, @0]
                    ,   @[@0, @0]
                    ,   @[@0]
                    ,   @[@0]
                    ,   @[]
                    ]
                ]
            ,   keyMisere : @[
                    // L0
                    @[
                        @[]
                    ,   @[]
                    ,   @[]
                    ,   @[]
                    ,   @[]
                    ,   @[]
                    ,   @[]
                    ,   @[]
                    ,   @[]
                    ,   @[]
                    ],
                    // L1
                    @[
                        @[@0, @0]
                    ,   @[@0]
                    ,   @[@0]
                    ,   @[@0]
                    ,   @[]
                    ,   @[]
                    ,   @[]
                    ,   @[]
                    ,   @[]
                    ,   @[]
                    ],
                    // L2
                    @[
                        @[@0, @0, @0, @0]
                    ,   @[@0, @0, @0]
                    ,   @[@0, @0]
                    ,   @[@0]
                    ,   @[@0]
                    ,   @[@0]
                    ,   @[]
                    ,   @[]
                    ,   @[]
                    ,   @[]
                    ],
                    // L3
                    @[
                        @[@0, @0, @0, @0, @0, @0, @0, @0]
                    ,   @[@0, @0, @0, @0, @0, @0, @0]
                    ,   @[@0, @0, @0, @0, @0, @0]
                    ,   @[@0, @0, @0, @0, @0]
                    ,   @[@0, @0, @0, @0]
                    ,   @[@0, @0, @0]
                    ,   @[@0, @0]
                    ,   @[@0]
                    ,   @[]
                    ,   @[]
                    ]
                ]
            };
        });
    }
    return dict;
}
